package vip.xiaonuo.smzq.core.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.oss.ServiceException;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.WKTReader2;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;


import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 图形创建工具
 *
 * @author liuliya
 * @date 2023-07-19
 */
public class GeometryUtil {

    private static final GeometryFactory geometryFactory = new GeometryFactory();

    private GeometryUtil() {
    }

    /**
     * 创建点
     *
     * @param coordinate
     * @return
     */
    public static Point createPoint(Coordinate coordinate) {
        return geometryFactory.createPoint(coordinate);
    }

    /**
     * 创建线
     *
     * @param coordinates
     * @return
     */
    public static LineString createLine(Coordinate[] coordinates) {
        return geometryFactory.createLineString(coordinates);
    }

    /**
     * 创建线性环
     *
     * @param coordinates
     * @return
     */
    public static LinearRing createLinearRing(Coordinate[] coordinates) {
        Coordinate[] destCoordinates = new Coordinate[coordinates.length + 1];
        System.arraycopy(coordinates, 0, destCoordinates, 0, coordinates.length);
        destCoordinates[coordinates.length] = coordinates[0];
        return geometryFactory.createLinearRing(destCoordinates);
    }


    /**
     * 创建面
     *
     * @param coordinates
     * @return
     */
    public static Polygon createPolygon(Coordinate[] coordinates) {
        Coordinate[] destCoordinates = new Coordinate[coordinates.length + 1];
        System.arraycopy(coordinates, 0, destCoordinates, 0, coordinates.length);
        destCoordinates[coordinates.length] = coordinates[0];
        return geometryFactory.createPolygon(destCoordinates);
    }

    /**
     * 创建带洞的面
     *
     * @param outerCoords
     * @param holeCoords
     * @return
     */
    public static Polygon createPolygonWithHole(Coordinate[] outerCoords, Coordinate[] holeCoords) {
        LinearRing outerBorder = createLinearRing(outerCoords);
        LinearRing innerBorder = createLinearRing(holeCoords);
        return geometryFactory.createPolygon(outerBorder, new LinearRing[]{innerBorder});
    }

    /**
     * 创建带多个洞的面
     *
     * @param outer
     * @param holes
     * @return
     */
    public static Polygon createPolygonWithHoles(LinearRing outer, LinearRing[] holes) {
        return geometryFactory.createPolygon(outer, holes);
    }

    /**
     * 创建多面
     *
     * @param polygons
     * @return
     */
    public static MultiPolygon createMultiPolygon(Polygon[] polygons) {
        return geometryFactory.createMultiPolygon(polygons);
    }


    /**
     * 根据坐标点创建多面
     *
     * @param geomArr 前台传递的特殊的点集合-多个
     *                114.38526 38.2561,117.2563 3652214
     * @return
     */
    public static MultiPolygon createMultiPolygonStr(List<String> geomArr) {
        //创建多面类型
        MultiPolygon multiPolygon = null;
        List<Polygon> polygonList = new ArrayList<>();
        for (String geom : geomArr) {
            //存储解析到的坐标
            List<Coordinate> coordinates = new ArrayList<>();
            try {
                String[] pointArry = geom.split(",");
                for (String point : pointArry) {
                    String[] xy = point.split(" ");
                    coordinates.add(new Coordinate(Double.parseDouble(xy[0]), Double.parseDouble(xy[1])));
                }
                //创建面
                Coordinate[] coordinateArry = coordinates.toArray(new Coordinate[coordinates.size()]);
                Polygon polygon = createPolygon(coordinateArry);
                polygonList.add(polygon);
            } catch (Exception e) {
                throw new ServiceException("坐标错误");
            }
        }
        if (ObjectUtil.isEmpty(polygonList)) {
            multiPolygon = createMultiPolygon(polygonList.toArray(new Polygon[polygonList.size()]));
            multiPolygon = (MultiPolygon) multiPolygon.union();
        }
        return multiPolygon;
    }

    /**
     * 多个面的WKT合并为一个WKT
     *
     * @param extentList 多个面的WKT
     * @return
     */
    public static String polygonWKTListToWKT(List<String> extentList) {
        WKTReader wktReader = new WKTReader();
        List<Polygon> polygonList = new ArrayList<>();
        if (ObjectUtil.isEmpty(extentList)) {
            throw new ServiceException("坐标为空");
        }
        for (String extent : extentList) {
            try {
                //读取WKT转为几何
                Geometry geometry = null;
                try {
                    geometry = wktReader.read(extent);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                geometry.setSRID(4490);
                //创建一个面
                Coordinate[] coordinates = geometry.getCoordinates();
                Polygon polygon = GeometryUtil.createPolygon(coordinates);
                polygonList.add(polygon);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException("几何坐标错误");
            }
        }
        Geometry union = GeometryUtil.createMultiPolygon(polygonList.toArray(new Polygon[polygonList.size()])).union();
        if (!union.isValid()) {
            throw new ServiceException("区域重叠或几何坐标错误");
        }
        return union.toText();
    }

    /**
     * WKT转几何
     *
     * @param wkt
     * @return
     * @throws ParseException
     */
    public static Geometry wkt2Geometry(String wkt) throws ParseException {
        WKTReader reader = new WKTReader();
        return reader.read(wkt);
    }

    /**
     * wkt 转 geojson
     *
     * @param wkt
     * @return
     * @throws Exception
     */
    public static String wkt2Geojson(String wkt) throws Exception {
        Geometry geometry = wkt2Geometry(wkt);
        return geometry2Geojson(geometry);
    }

    /**
     * 几何转json
     *
     * @param geometry
     * @return
     * @throws IOException
     */
    public static String geometry2Geojson(Geometry geometry) throws IOException {
        GeometryJSON gjson = new GeometryJSON(7);
        StringWriter writer = new StringWriter();
        gjson.write(geometry, writer);
        return writer.toString();
    }

    /**
     * geojson 转 wtk
     *
     * @param geojson
     * @return
     * @throws Exception
     */
    public static String geojson2Wkt(String geojson) throws Exception {
        Geometry geometry = geojson2Geometry(geojson);
        return geometry2Wkt(geometry);
    }

    /**
     * geojson转几何
     *
     * @param geojson
     * @return
     * @throws IOException
     */
    public static Geometry geojson2Geometry(String geojson) throws IOException {
        GeometryJSON gjson = new GeometryJSON(7);
        return gjson.read(new StringReader(geojson));
    }

    /**
     * 读取几何
     *
     * @param geometry
     * @return
     * @throws ParseException
     */
    public static String geometry2Wkt(Geometry geometry) throws ParseException {
        WKTWriter writer = new WKTWriter();
        return writer.write(geometry);
    }

    /**
     * 根据坐标点处理成WKT
     * 坐标数据处理成WKT
     *
     * @param geomArr 坐标数据
     */
    public static Geometry geomDispose(List<Object> geomArr) {
        if (ObjectUtil.isNull(geomArr)) {
            return null;
        }
        List<Polygon> geometryList = new ArrayList<>();
        //添加面的闭合点
        addClosedPoint(geomArr);
        for (Object o : geomArr) {
            Map<String, Object> geoJsonMap = new HashMap<>();
            geoJsonMap.put("type", "Polygon");
            geoJsonMap.put("coordinates", o);
            String geoJson = JSONUtil.toJsonStr(geoJsonMap);
            try {
                geometryList.add((Polygon) GeometryUtil.geojson2Geometry(geoJson));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (ObjectUtil.isEmpty(geometryList)) {
            return null;
        }
        return createMultiPolygon(geometryList.toArray(new Polygon[geometryList.size()])).union();
    }

    /**
     * 添加闭合点
     *
     * @param geomArr
     */
    public static void addClosedPoint(List<Object> geomArr) {
        for (Object o : geomArr) {
            try {
                List<List<Double>> list = (List<List<Double>>) o;
                //判断第一个点和最后一个点是否一致
                List<Double> pointIndex = list.get(0);
                List<Double> pointLast = list.get(list.size() - 1);
                //不一致添加闭合点
                if (!pointIndex.get(0).equals(pointLast.get(0)) || !pointIndex.get(1).equals(pointLast.get(1))) {
                    list.add(list.get(0));
                }
            } catch (ClassCastException e) {
                addClosedPoint((List<Object>) o);
            }
        }
    }

    /**
     * 读取WKT格式数据
     *
     * @param WKT
     * @return
     */
    public static Geometry readWKT(String WKT) {
        //读取坐标
        WKTReader2 wktReader = new WKTReader2();
        try {
            return wktReader.read(WKT);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }

}
